import logging
import os
from ftplib import FTP
from queue import Queue
from typing import List

from config.ServerApp import app


class FtpUtil:
    def __init__(self, config):
        # 从配置字典中读取FTP连接信息
        self.host = config.get('host')
        self.port = int(config.get('port', 21))
        self.username = config.get('username')
        self.password = config.get('password')
        self.pasv_mode = config.get('pasvMode', True)  # 默认为主动模式
        self.encoding = config.get('encoding', 'utf-8')  # 默认字符集为utf-8
        self.max_connections = int(config.get('maxConnections', 1))  # 默认连接池数量为1
        self.connection_pool = Queue(self.max_connections)
        self.logger = config.get('logger', app.ctx.logger)
        self._initialize_pool()

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            self.close_all_connections()
        except Exception as e:
            pass

    def _initialize_pool(self):
        # 初始化连接池，创建指定数量的FTP连接并放入队列中
        for _ in range(self.max_connections):
            ftp = FTP()
            ftp.connect(self.host, self.port)
            ftp.login(self.username, self.password)
            ftp.set_pasv(self.pasv_mode)  # 设置主被动模式
            ftp.encoding = self.encoding  # 设置字符集
            self.connection_pool.put(ftp)

    def _get_connection(self):
        ftp = self.connection_pool.get()
        if not self._is_connection_valid(ftp):
            self.logger.warning("Invalid connection found, attempting to reconnect...")
            ftp = self._reconnect()
        return ftp

    def _is_connection_valid(self, ftp):
        # 检查FTP连接是否有效
        try:
            ftp.voidcmd("NOOP")
            return True
        except:
            return False

    def _reconnect(self):
        # 尝试重新连接FTP服务器
        ftp = FTP()
        try:
            ftp.connect(self.host, self.port)
            ftp.login(self.username, self.password)
            ftp.set_pasv(self.pasv_mode)
            ftp.encoding = self.encoding
            self.logger.info("Successfully reconnected to FTP server.")
        except Exception as e:
            self.logger.error(f"Failed to reconnect to FTP server: {e}")
            raise e
        return ftp

    def _return_connection(self, ftp):
        self.connection_pool.put(ftp)

    def download_file(self, remote_path, local_path):
        # 从FTP服务器下载单个文件到本地
        ftp = self._get_connection()
        try:
            self.logger.info(f"Downloading file {remote_path} to {local_path}")
            with open(local_path, 'wb') as local_file:
                ftp.retrbinary(f'RETR {remote_path}', local_file.write)
        except Exception as e:
            self.logger.error(f"Failed to download file {remote_path}: {e}")
            raise e
        finally:
            self._return_connection(ftp)

    def download_directory(self, remote_dir, local_dir):
        # 从FTP服务器递归下载整个目录到本地
        ftp = self._get_connection()
        try:
            if not os.path.exists(local_dir):
                os.makedirs(local_dir)
            self._recursive_download(ftp, remote_dir, local_dir)
        except Exception as e:
            self.logger.error(f"Failed to download directory {remote_dir}: {e}")
            raise e
        finally:
            self._return_connection(ftp)



    def walk_directory(self, remote_dir) -> List[str]:
        """
        遍历FTP目录 返回目录下的文件列表,包括子目录
        """
        ftp = self._get_connection()
        try:
            return self._recursive_walk(ftp, remote_dir)
        except Exception as e:
            self.logger.error(f"Failed to walk directory {remote_dir}: {e}")
            raise e
        finally:
            self._return_connection(ftp)

    def _recursive_walk(self, ftp, remote_dir) -> List[str]:
        # 递归遍历目录中的所有文件和子目录
        items = ftp.nlst(remote_dir)
        full_paths = []
        for item in items:
            full_path = os.path.join(remote_dir, item)
            if self._is_directory(ftp, full_path):
                full_paths.extend(self._recursive_walk(ftp, full_path))
            else:
                full_paths.append(full_path)
        return full_paths

    def _recursive_download(self, ftp, remote_dir, local_dir):
        # 递归下载目录中的所有文件和子目录
        items = ftp.nlst(remote_dir)
        for item in items:
            try:
                if self._is_directory(ftp, item):
                    new_remote_dir = item
                    new_local_dir = os.path.join(local_dir, os.path.basename(item))
                    if not os.path.exists(new_local_dir):
                        os.makedirs(new_local_dir)
                    self._recursive_download(ftp, new_remote_dir, new_local_dir)
                else:
                    local_file_path = os.path.join(local_dir, os.path.basename(item))
                    self.download_file(item, local_file_path)
            except Exception as e:
                self.logger.error(f"Failed to process item {item}: {e}")
                raise e

    def _is_directory(self, ftp, path):
        # 判断给定的路径是否为目录
        try:
            ftp.cwd(path)
            ftp.cwd('..')
            return True
        except:
            return False

    def close_all_connections(self):
        # 关闭连接池中的所有FTP连接
        while not self.connection_pool.empty():
            ftp = self.connection_pool.get()
            ftp.quit()
